#include "qpropertylistview.h"

#include "qproperty.h"
#include "qobjectmanager.h"
#include "qproperty.h"
#include "qobjectproperty.h"
#include "qpropertycorecreator.h"
#include "qresetwidget.h"

#include <QHeaderView>
#include <QPainter>
#include <QStyleOptionViewItemV3>
#include <QApplication>
#include <QKeyEvent>
#include <QLineEdit>

static QIcon drawIndicatorIcon(const QPalette &palette,QStyle *style)
{
    QPixmap pix(14,14);

    pix.fill(Qt::transparent);

    QStyleOption branchOption;
    QRect r(QPoint(0,0),pix.size());
    branchOption.rect=QRect(2,2,9,9);
    branchOption.palette=palette;
    branchOption.state=QStyle::State_Children;

    QPainter p;
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch,&branchOption,&p);
    p.end();
    QIcon rc=pix;
    rc.addPixmap(pix,QIcon::Selected,QIcon::Off);
    branchOption.state |=QStyle::State_Open;

    pix.fill(Qt::transparent);
    p.begin(&pix);
    style->drawPrimitive(QStyle::PE_IndicatorBranch,&branchOption,&p);
    p.end();

    rc.addPixmap(pix,QIcon::Normal,QIcon::On);
    rc.addPixmap(pix,QIcon::Selected,QIcon::On);
    return rc;
}

QWidget *QPropertyEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &index) const
{
    QWidget *ret=NULL;
    if(index.column()==1)
    {
        QTreeWidgetItem *item=m_listView->indexToItem(index);
        QProperty *property=m_listView->itemToProperty(item);
        if(property!=NULL)
        {
            m_editedItem.insert(0,item);
            ret=m_listView->createEditor(parent,property);
            if(ret!=NULL)
            {
                QResetWidget *wid=new QResetWidget(property,parent);
                wid->setWidget(ret);
                wid->enabledButton(property->modified());
                wid->setReset(property->reset());
                ret=wid;
                connect(ret,SIGNAL(destroyed(QObject*)),this,SLOT(slotEditorDestroyed(QObject*)));
                m_editor=ret;
            }
        }
    }
    return ret;
}

void QPropertyEditorDelegate::closeEditor(QProperty* /*property*/)
{
    if(m_editor!=NULL)
    {
        m_editor->deleteLater();
    }
}

void QPropertyEditorDelegate::slotEditorDestroyed(QObject *object)
{
    if(qobject_cast<QWidget*>(object)!=NULL)
    {
        m_editedItem.removeLast();
        m_editor=NULL;
    }
}

QTreeWidgetItem *QPropertyEditorDelegate::editerItem()
{
    return m_editedItem.size()>0?m_editedItem.first():NULL;
}

void QPropertyEditorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const
{
    editor->setGeometry(option.rect.adjusted(0,0,0,-1));
}

void QPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QTreeWidgetItem* item=m_listView->indexToItem(index);
    QStyleOptionViewItemV3 opt=option;
    QColor c;
    if(item!=NULL)
    {
        QProperty *p=m_listView->itemToProperty(item);

        if(p!=NULL)
        {
            if(p->modified() && index.column()==0 && p->parent()==NULL)
            {
                opt.font.setBold(true);
                opt.fontMetrics=QFontMetrics(opt.font);
            }
            QModelIndex temp=index;
            while(temp.isValid() && !m_listView->m_groupItems.contains(m_listView->itemFromIndex(temp)))
            {
                temp=temp.parent();
            }
            if(temp.isValid())
            {
                QTreeWidgetItem *tempItem=m_listView->itemFromIndex(temp);
                c=m_listView->m_colors.at(m_listView->m_groupItems.indexOf(tempItem)%m_listView->m_colors.count()).first;
            }
            else
            {
                c=m_listView->m_colors.at(0).first;
            }

            if(opt.features & QStyleOptionViewItemV2::Alternate)
            {
                c=c.lighter(112);
            }

        }
        else
        {
            c=opt.palette.color(QPalette::Dark);
            opt.palette.setColor(QPalette::Text,opt.palette.color(QPalette::BrightText));
        }
    }



    painter->fillRect(option.rect,c);
    opt.state &=~ QStyle::State_HasFocus;
    QItemDelegate::paint(painter,opt,index);
    opt.palette.setCurrentColorGroup(QPalette::Active);
    QColor color=static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor,&opt));
    painter->setPen(color);

    if(!m_listView->m_groupItems.contains(item))
    {
        if(index.column()==0)
        {
            int right=option.direction==Qt::LeftToRight?option.rect.right():option.rect.left();
            painter->drawLine(right,option.rect.y(),right,option.rect.bottom());
        }
    }
}

QSize QPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size=QItemDelegate::sizeHint(option,index);
    size.setHeight(21);
    size.rwidth()+=4;
    return size;
    //return +QSize(3,4);
}

bool QPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event)
{
    return QItemDelegate::eventFilter(object,event);
}




QPropertyListView::QPropertyListView(AbstractCore *core, QWidget *parent):
    QTreeWidget(parent),
    m_object(NULL),
    m_core(core)
{
    qRegPropertyCreator(new QPropertyCoreCreator);
    this->setFrameStyle(QFrame::NoFrame);
    QVector<QColor> colors;
    colors.reserve(6);
    colors.push_back(QColor(255,230,191));
    colors.push_back(QColor(255,255,191));
    colors.push_back(QColor(191,255,191));
    colors.push_back(QColor(199,255,255));
    colors.push_back(QColor(234,191,255));
    colors.push_back(QColor(255,191,239));
    m_colors.reserve(colors.count());
    const int darknessFactor=250;
    for(int i=0;i<colors.count();i++)
    {
        QColor c=colors.at(i);
        m_colors.push_back(qMakePair(c,c.darker(darknessFactor)));
    }

    connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int)));
    setIconSize(QSize(18, 18));
    setColumnCount(2);
    QStringList labels;
    labels.append(tr("Property"));
    labels.append(tr("Value"));
    setHeaderLabels(labels);
    setAlternatingRowColors(true);
    setEditTriggers(QAbstractItemView::EditKeyPressed);
    setRootIsDecorated(false);

    m_manager=new QObjectManager(m_core);
    connect(m_manager,SIGNAL(changed(QObjectProperty*,QProperty*)),this,SLOT(valueChanged(QObjectProperty*,QProperty*)));

    m_delegate=new QPropertyEditorDelegate(this);
    setItemDelegate(m_delegate);
    header()->setMovable(false);

    m_expandIcon=drawIndicatorIcon(this->palette(),this->style());


}

QTreeWidgetItem * QPropertyListView::addGroup(const QString &group)
{
    QTreeWidgetItem *item;

    if(m_groupList.contains(group))
    {
        return m_groupItems.at(m_groupList.indexOf(group));
    }

    item=new QTreeWidgetItem(this,NULL);
    item->setData(0,Qt::DisplayRole,group);
    m_groupItems.append(item);
    m_groupList.append(group);
    updateItem(item);
    return item;
}

QTreeWidgetItem* QPropertyListView::groupItem(const QString &group)
{
    if(m_groupList.contains(group))
    {
        return m_groupItems.at(m_groupList.indexOf(group));
    }
    else
    {
        return NULL;
    }
}

void QPropertyListView::insertItem(QProperty *pro, QTreeWidgetItem *parent)
{
    QTreeWidgetItem *item=new QTreeWidgetItem(parent);

    m_items.insert(pro,item);
    item->setFlags(item->flags() | Qt::ItemIsEditable);
}

void QPropertyListView::setObject(QObject *object)
{
    if(m_object!=NULL)
    {
        disconnect(m_object,SIGNAL(destroyed()),this,SLOT(objectDestory()));
    }
    this->clear();
    m_items.clear();
    m_groupItems.clear();
    m_groupList.clear();
    m_object=NULL;
    m_objectProperty=m_manager->getObjectProperty(object);

    QList<QProperty*> propertys=m_objectProperty->propertys();

    QTreeWidgetItem* item;
    QProperty *p;
    QList<QProperty*> prolist;
    for(int i=0;i<propertys.size();i++)
    {
        p=propertys.at(i);
        if(p->parent()==NULL)
        {
            item=addGroup(p->group());
            prolist=p->children();
            insertItem(p,item);
            QProperty *temp;
            while(prolist.size()>0)
            {
                temp=prolist.first();
                insertItem(temp,m_items.value(temp->parent()));
                prolist.removeFirst();
                prolist+=temp->children();
            }
        }
    }
    m_object=object;
    updateValue();
    for(int i=0;i<m_groupItems.size();i++)
    {
        updateItem(m_groupItems.at(i));
    }
    this->expandAll();
    connect(m_object,SIGNAL(destroyed()),this,SLOT(objectDestory()));
}

QTreeWidgetItem *QPropertyListView::indexToItem(const QModelIndex &index) const
{
    return itemFromIndex(index);
}

QProperty * QPropertyListView::itemToProperty(QTreeWidgetItem *item) const
{
    QMapIterator<QProperty*,QTreeWidgetItem*> i(m_items);
    while(i.hasNext())
    {
        i.next();
        if(i.value()==item)
        {
            return i.key();
        }
    }
    return NULL;
}

void QPropertyListView::updateValue()
{
    QMapIterator<QProperty*,QTreeWidgetItem*> i(m_items);
    while (i.hasNext()) {
        i.next();
        updateItem(i.value());
    }
}

void QPropertyListView::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
{
    QTreeWidgetItem* item=indexToItem(index);
    QStyleOptionViewItemV3 opt=options;
    QColor c;
    if(item!=NULL)
    {
        QProperty *p=this->itemToProperty(item);
        if(p!=NULL)
        {
            c=m_colors.at(m_groupList.indexOf(p->group())%m_colors.count()).first;
            painter->fillRect(options.rect,c);
            opt.palette.setColor(QPalette::AlternateBase,c.lighter(112));
        }
        else
        {
            c=opt.palette.color(QPalette::Dark);
            painter->fillRect(options.rect,c);
            opt.palette.setColor(QPalette::AlternateBase,c);
        }
    }


    QTreeWidget::drawRow(painter,opt,index);

    QColor color=static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor));
    painter->setPen(color);
    painter->drawLine(opt.rect.x(),opt.rect.bottom(),opt.rect.right(),opt.rect.bottom());

}

void QPropertyListView::updateItem(QTreeWidgetItem *item)
{
    QProperty *p=itemToProperty(item);
    QIcon expanIcon;
    if(!rootIsDecorated() && p==NULL)
    {
        expanIcon=m_expandIcon;
    }
    if(p!=NULL)
    {
        //p->setValue(m_object->property(p->name().toLocal8Bit()));
        item->setToolTip(1,p->valueText());
        item->setText(1,p->valueText());
        item->setIcon(1,p->valueIcon());
        item->setText(0,p->name());
        item->setToolTip(0,p->name());

    }
    else
    {
        QString text=m_groupList.at(m_groupItems.indexOf(item));
        item->setToolTip(0,text);
        item->setText(0,text);
    }
    item->setIcon(0,expanIcon);
    item->setFirstColumnSpanned(p==NULL);

    bool wasEnabled=item->flags() & Qt::ItemIsEnabled;
    bool isEnabled=wasEnabled;
    if(p!=NULL)
    {
        if(p->enabled())
        {
            QTreeWidgetItem *parent=item->parent();
            if(!parent || (parent->flags()) & Qt::ItemIsEnabled)
            {
                isEnabled=true;
            }
            else
            {
                isEnabled=false;
            }
        }
        else
        {
            isEnabled=false;
        }
    }
    else
    {
        isEnabled=true;
    }
    if(wasEnabled!=isEnabled)
    {
        if(isEnabled)
        {
            enableItem(item);
        }
        else
        {
            disableItem(item);
        }
    }
    viewport()->update();
}

void QPropertyListView::enableItem(QTreeWidgetItem *item) const
{
    Qt::ItemFlags flags=item->flags();

    flags |=Qt::ItemIsEnabled;
    item->setFlags(flags);
    for(int i=0;i<item->childCount();i++)
    {
        QTreeWidgetItem *child=item->child(i);
        QProperty *property=itemToProperty(child);
        if(property!=NULL && property->enabled())
        {
            enableItem(child);
        }
    }

}

void QPropertyListView::disableItem(QTreeWidgetItem *item) const
{
    Qt::ItemFlags flags=item->flags();

    if(flags & Qt::ItemIsEnabled)
    {
        flags &= ~Qt::ItemIsEnabled;
        item->setFlags(flags);
        m_delegate->closeEditor(itemToProperty(item));
        for(int i=0;i<item->childCount();i++)
        {
            QTreeWidgetItem *child=item->child(i);
            QProperty *property=itemToProperty(child);
            if(property!=NULL && property->enabled())
            {
                disableItem(child);
            }
        }
    }
}

void QPropertyListView::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
    case Qt::Key_Return:
    case Qt::Key_Enter:
    case Qt::Key_Space:
        if(!m_delegate->editerItem())
        {
            QTreeWidgetItem *item=currentItem();
            if(item->columnCount()>=2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled))==(Qt::ItemIsEditable | Qt::ItemIsEnabled)))
            {
                event->accept();
                QModelIndex index=currentIndex();
                if(index.column()==0)
                {
                    index=index.sibling(index.row(),1);
                    setCurrentIndex(index);
                }
                edit(index);
            }
        }
        break;
    }
    QTreeWidget::keyPressEvent(event);
}

void QPropertyListView::mousePressEvent(QMouseEvent *event)
{
    QTreeWidget::mousePressEvent(event);

    QTreeWidgetItem *item=itemAt(event->pos());

    if(item)
    {
        if(item!=m_delegate->editerItem() && (event->button()==Qt::LeftButton)
                && (header()->logicalIndexAt(event->pos())==1)
                && ((item->flags() &(Qt::ItemIsEditable | Qt::ItemIsEnabled))==(Qt::ItemIsEditable | Qt::ItemIsEnabled)))
        {
            editItem(item,1);
        }
        else if(m_groupItems.contains(item))
        {
            if(event->pos().x()+header()->offset()<20)
            {
                item->setExpanded(!item->isExpanded());
            }
        }
    }
}

QWidget *QPropertyListView::createEditor(QWidget *parent, QProperty *property)
{
    return m_manager->createEditor(parent,property);
}

void QPropertyListView::valueChanged(QObjectProperty *pro, QProperty *property)
{
    QTreeWidgetItem* item;
    if(m_object==pro->object())
    {
        m_object->setProperty(property->name().toLocal8Bit(),property->value());
        updateItem(m_items.value(property));
        QList<QProperty*>   list=property->children();
        while(list.size()>0)
        {
            item=m_items.value(list.first());
            //if(item!=m_delegate->editerItem())
            {
                updateItem(item);
            }
            list+=list.first()->children();
            list.removeFirst();
        }
    }
    if(property->name()=="objectName")
    {
        emit nameChanged();
    }
    emit changed();
}

void QPropertyListView::updateProperty(const QString &property)
{
    QProperty *p=m_objectProperty->getProperty(property);
    if(p!=NULL)
    {
        if(p->value()!=m_object->property(property.toLocal8Bit()))
        {
            p->setValue(m_object->property(property.toLocal8Bit()));
            p->setModified(true);
            updateItem(m_items.value(p));
        }

    }
}

QObjectProperty* QPropertyListView::getObjectProperty(QObject *object)
{
    return m_manager->getObjectProperty(object);
}

QObject* QPropertyListView::object()
{
    return m_object;
}

void QPropertyListView::objectDestory()
{
    this->clear();
    m_items.clear();
    m_groupItems.clear();
    m_groupList.clear();
    m_object=NULL;
}
